#region Using Statements
using System;
using System.Timers;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
#endregion

namespace SkyBurner
{
    #region VolumetricCloud class
    public class VolumetricCloud : BaseDrawableObject
    {
        protected Vector3 m_vPosition;
        protected Vector3 m_worldSunLightPosition;
        protected Texture2D m_particleTexture;
        protected int m_nParticleCount;
        protected BoundingBoxEx m_boundingVolume;
        protected Vector3[] m_particlePositions;
        protected VolumetricCloudShader m_volumetricCloudShader;
        
        public VolumetricCloud(BaseGame pGame, BaseWorld pWorld, int nRandomSeed, Vector3 pPosition, int pParticleCount, BoundingBoxEx pBoundingVolume, Texture2D pParticleTexture)
            : base(pGame)
        {
            Random rnd = new Random(nRandomSeed);

            m_vPosition = pPosition;
            m_nParticleCount = pParticleCount;
            m_particleTexture = pParticleTexture;
            m_boundingVolume = pBoundingVolume;

            // initialize particle into the cloud volume...
            m_particlePositions = new Vector3[m_nParticleCount];
            for (int i = 0; i < m_nParticleCount; i++)
            {
                Vector3 vRandomPos = new Vector3(m_boundingVolume.Max.X * (float)rnd.NextDouble(), m_boundingVolume.Max.Y * (float)rnd.NextDouble(), m_boundingVolume.Max.Z * (float)rnd.NextDouble());

                m_particlePositions[i].X = m_vPosition.X + ((m_boundingVolume.Max.X * 0.5f) - vRandomPos.X);
                m_particlePositions[i].Y = m_vPosition.Y + ((m_boundingVolume.Max.Y * 0.5f) - vRandomPos.Y);
                m_particlePositions[i].Z = m_vPosition.Z + ((m_boundingVolume.Max.Z * 0.5f) - vRandomPos.Z);
            }

            // load cloud shader...
            m_volumetricCloudShader = (VolumetricCloudShader)m_Game.ShadersManager.GetShader("VolumetricCloudShader");

            // get sunlight position...
            BaseWorldLight sunLight = pWorld.GetLight("SunLight");
            if (sunLight != null)
                m_worldSunLightPosition = sunLight.Position;
        }

        public override void Update(GameTime gameTime, Camera pActiveCamera)
        {
            base.Update(gameTime, pActiveCamera);
        }

        public override void Draw(GameTime gameTime, Camera pActiveCamera)
        {
            base.Draw(gameTime, pActiveCamera);

            m_Game.GraphicsDevice.RenderState.CullMode = CullMode.None;

            m_Game.GraphicsDevice.RenderState.AlphaBlendEnable = true;
            m_Game.GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add;
            m_Game.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            m_Game.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;

            m_Game.GraphicsDevice.RenderState.DepthBufferEnable = true;
            m_Game.GraphicsDevice.RenderState.DepthBufferWriteEnable = false;

            //m_Game.GraphicsDevice.RenderState.AlphaTestEnable = true;
            //m_Game.GraphicsDevice.RenderState.AlphaFunction = CompareFunction.Greater;
            //m_Game.GraphicsDevice.RenderState.ReferenceAlpha = 0x0F;

            for (int i = 0; i < m_nParticleCount; i++)
                m_volumetricCloudShader.Draw(gameTime, pActiveCamera, m_vPosition + m_worldSunLightPosition, m_vPosition, m_particlePositions[i], 5.0f, m_particleTexture);

            //m_Game.GraphicsDevice.RenderState.AlphaTestEnable = false;
            m_Game.GraphicsDevice.RenderState.AlphaBlendEnable = false;
            m_Game.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
        }
    }
    #endregion

    #region VolumetricCloudsManager class
    public class VolumetricCloudsManager : SkyBurnerWorldObject
    {
        private Random m_rnd;
        private float m_nMaxClouds;
        private float m_fSpawnInterval;
        private float m_fCloudLifeTime;
        private float m_fLastSpawnTime;
        private Collection<Pair<float, VolumetricCloud>> m_cloudsList = new Collection<Pair<float, VolumetricCloud>>();

        public VolumetricCloudsManager(BaseGame pGame, SkyBurnerWorld pSkyBurnerWorld, int pMaxClouds, float pSpawnInterval, float pCloudLifeTime)
            : base(pGame, pSkyBurnerWorld)
        {
            m_nMaxClouds = pMaxClouds;
            m_fSpawnInterval = pSpawnInterval;
            m_fCloudLifeTime = pCloudLifeTime;
            m_fLastSpawnTime = 0.0f;
            m_rnd = new Random();
        }

        public override void Update(GameTime gameTime, Camera pActiveCamera)
        {
            base.Update(gameTime, pActiveCamera);

            // remove dead clouds...
            int i = 0;
            while (i < m_cloudsList.Count)
            {
                m_cloudsList[i].First -= gameTime.ElapsedGameTime.Milliseconds;
                if ( m_cloudsList[i].First <= 0.0f)
                {
                    m_cloudsList.RemoveAt(i);
                    continue;
                }
                i++;
            }

            // create new clouds if needed...
            if (m_cloudsList.Count < m_nMaxClouds)
            {
                m_fLastSpawnTime += (float)gameTime.ElapsedGameTime.Milliseconds;
                if (m_fLastSpawnTime > m_fSpawnInterval)
                {
                    Vector3 vPosition = new Vector3((0.5f - (float)m_rnd.NextDouble()) * 100.0f, 2.0f, pActiveCamera.Position.Z + 300.0f);
                    VolumetricCloud volumetricCloud = new VolumetricCloud(m_Game,
                                                                          m_skyBurnerWorld, 
                                                                          (int)gameTime.TotalRealTime.TotalMilliseconds,                                                                           
                                                                          vPosition, 
                                                                          20, 
                                                                          new BoundingBoxEx(Vector3.Zero, new Vector3(5.0f, 0.5f, 2.0f)), 
                                                                          m_Game.ContentManager.Load<Texture2D>("Data\\Textures\\smoke1"));

                    m_cloudsList.Add(new Pair<float, VolumetricCloud>(m_fCloudLifeTime, volumetricCloud));

                    m_fLastSpawnTime = 0.0f;
                }
            }
        }

        public override void Draw(GameTime gameTime, Camera pActiveCamera)
        {
            base.Draw(gameTime, pActiveCamera);

            for (int i = 0; i < m_cloudsList.Count; i++)
                m_cloudsList[i].Second.Draw(gameTime, pActiveCamera);
        }
    }
    #endregion
}
